package chapters15_bingfa.chapters15_3;

/**
 * Created by xinxuchu on 2020/1/29.
 * 异步结果
 * 在主从模式中，手工创建线程往往比较麻烦，
 * 一种常见的模式是异步调用，异步调用返回一个一般称为Future的对象，通过它可以获得最终的结果。
 */
public class MyExecutor {

    /**
     * 表示子任务的接口是Callable
     * @param <V>
     */
    public interface Callable<V>{
        V call() throws Exception;
    }

    /**
     * 为表示异步调用的结果，我们定义一个接口MyFuture
     * 这个接口的get方法返回真正的结果，如果结果还没有计算完成，
     * get方法会阻塞直到计算完成，如果调用过程发生异常，则get方法抛出调用过程中的异常。
     * @param <V>
     */
    public interface MyFuture<V>{
        V get() throws  Exception;
    }

    /**
     * 为方便主线程调用子任务，我们定义一个类MyExecutor，
     * 其中定义一个public方法execute，表示执行子任务并返回异步结果，声明如下：
     * @param task
     * @param <V>
     * @return
     */
    public <V> MyFuture<V> excute(final Callable<V> task){

        final Object lock = new Object();
        final ExecuteThread<V> thread = new ExecuteThread<>(task,lock);
        thread.start();

        MyFuture<V> future = new MyFuture<V>() {
            @Override
            public V get() throws Exception {
                synchronized (lock){
                    while (!thread.isDone()){
                        try {
                            lock.wait();
                        }catch (InterruptedException e){
                        }
                    }
                    if (thread.getException()!=null){
                        throw thread.getException();
                    }
                    return thread.getResult();
                }
            }
        };
        return future;
    }

    static class ExecuteThread<V> extends Thread{
        /**
         * 这个子线程执行实际的子任务，记录执行结果到result变量、异常到exception变量，
         * 执行结束后设置共享状态变量done为true，并调用notifyAll，以唤醒可能在等待结果的主线程。
         */
        private V result = null;
        private Exception exception = null;
        private boolean done = false;
        private Callable<V> task;
        private Object lock;

        public ExecuteThread(Callable<V> task,Object lock){
            this.task = task;
            this.lock = lock;
        }

        @Override
        public void run(){
            try {
                result = task.call();

            }catch (Exception e){
                exception = e;
            }finally {
                synchronized (lock){
                    done = true;
                    lock.notifyAll();
                }
            }
        }

        public V getResult() {
            return result;
        }

        public Exception getException() {
            return exception;
        }

        public boolean isDone() {
            return done;
        }
    }

    public static void main(String[] args) {
        MyExecutor executor = new MyExecutor();
        Callable<Integer> subTask = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int milliis = (int)(Math.random()*1000);
                Thread.sleep(milliis);
                return milliis;
            }
        };

        //异步调用，返回一个MyFture对象
        MyFuture<Integer> future = executor.excute(subTask);
        //执行其他操作..........
        try {

            Integer result = future.get();
            System.out.println(result);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}
